package com.example.fashionshoes.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.fashionshoes.constant.JwtConst;
import com.example.fashionshoes.exception.*;
import com.example.fashionshoes.mapper.UserMapper;
import com.example.fashionshoes.pojo.dto.UserDTO;
import com.example.fashionshoes.pojo.dto.UserInfoDTO;
import com.example.fashionshoes.pojo.dto.UserPasswordDTO;
import com.example.fashionshoes.pojo.po.AddressBook;
import com.example.fashionshoes.pojo.po.User;
import com.example.fashionshoes.pojo.vo.UserLoginVo;
import com.example.fashionshoes.pojo.vo.UserVo;
import com.example.fashionshoes.service.AddressBookService;
import com.example.fashionshoes.service.UserService;
import com.example.fashionshoes.utils.JwtUtil;
import com.example.fashionshoes.utils.Md5Util;
import com.example.fashionshoes.utils.ThreadLocalUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private AddressBookService addressBookService;

    /**
     * 用户登录
     *
     * @param user 用户账户密码
     * @return 用户信息（不包含密码）
     */
    @Override
    public UserLoginVo toLogin(User user) throws Exception {
        //Md5加密验证
        String md5Password = Md5Util.getMD5String(user.getPassword());
        //查询账户对应的密码
        User users = lambdaQuery().eq(User::getUsername, user.getUsername()).eq(User::getPassword, md5Password).one();
        if (users == null) {
            //查询账户
            User user1 = lambdaQuery().eq(User::getUsername, user.getUsername()).one();
            if (user1 == null) {
                //账户不存在
                throw new UserNotFoundException("账户不存在");
            } else {
                //密码错误
                throw new PasswordIncorrectException("密码错误");
            }
        }
        if (users.getStatus() == 0){
            throw new BusinessException("该账户已被禁用，请联系管理员");
        }
        //擦除密码
        users.setPassword(null);
        //发放token
        return getUserLoginAndToken(users);
    }

    /**
     * 用户注册
     *
     * @param user 用户填写的数据
     * @return 用户信息
     */
    @Override
    public UserLoginVo toRegister(User user) throws UsernameExistException, PasswordNotMatchException {
        if (!user.getRePassword().equals(user.getPassword())) {
            throw new PasswordNotMatchException("前后密码不一致");
        } else {
            //进行密码加密
            String md5Password = Md5Util.getMD5String(user.getPassword());
            user.setPassword(md5Password);
            try {
                this.save(user);
            } catch (DuplicateKeyException e) {
                throw new UsernameExistException("用户名已存在");
            } catch (Exception e) {
                // 记录日志并重新抛出异常
                log.error("用户注册失败", e);
                throw new RuntimeException("用户注册失败，请稍后重试");
            }
        }
        return getUserLoginAndToken(user);
    }

    /**
     * 修改密码
     *
     * @param userPasswordDTO 要修改的密码
     */
    @Override
    public void changePassword(UserPasswordDTO userPasswordDTO) throws PasswordNotMatchException, PasswordIncorrectException, SamePasswordException {
        //判断新密码和确认密码一致
        if (!userPasswordDTO.getNewPassword().equals(userPasswordDTO.getRePassword())) {
            throw new PasswordNotMatchException("前后密码不一致");
        }
        //判断新密码是否和旧密码一样
        if (userPasswordDTO.getOldPassword().equals(userPasswordDTO.getNewPassword())) {
            throw new SamePasswordException("新旧密码一致");
        }
        Map<String, Object> map = ThreadLocalUtil.get();
        Integer userId = (Integer) map.get(JwtConst.USER_ID);
        //旧密码是否正确
        String md5OldPassword = Md5Util.getMD5String(userPasswordDTO.getOldPassword());
        User user = lambdaQuery().eq(User::getPassword, md5OldPassword).eq(User::getId, userId).one();
        if (user == null) {
            throw new PasswordIncorrectException("密码错误");
        }
        //修改密码
        String md5NewPassword = Md5Util.getMD5String(userPasswordDTO.getNewPassword());
        lambdaUpdate().eq(User::getId, userId).set(User::getPassword, md5NewPassword).update();
    }

    /**
     * 修改用户信息
     *
     * @param userDTO 用户信息
     * @return 修改后的用户信息
     */
    @Override
    public UserVo modifyUserInfo(UserDTO userDTO) {
        Map<String, Object> map = ThreadLocalUtil.get();
        Integer userId = (Integer) map.get(JwtConst.USER_ID);
        lambdaUpdate().set(User::getUsername, userDTO.getUsername())
                .set(User::getPhone, userDTO.getPhone())
                .set(User::getSignature, userDTO.getSignature())
                .set(User::getEmail, userDTO.getEmail())
                .eq(User::getId, userId)
                .update();
        //修改默认地址
        addressBookService.lambdaUpdate().set(AddressBook::getIsDefault,0)
                .eq(AddressBook::getId,userDTO.getAddressId())
                .eq(AddressBook::getUserId,userId)
                .update();
        return BeanUtil.copyProperties(userDTO, UserVo.class);
    }

    /**
     * 禁用启用用户账户
     * @param userInfoDTO 用户id,status
     * @return 操作的结果
     */
    @Override
    public boolean modifyUserStatus(UserInfoDTO userInfoDTO) {
        boolean update;
        if (userInfoDTO.getStatus()==1) {
            //冻结用户账户
            update= lambdaUpdate().eq(User::getId, userInfoDTO.getId()).set(User::getStatus, 0).update();
            //从redis中移除token
            stringRedisTemplate.delete(JwtConst.USER_TOKEN_CATCH+userInfoDTO.getId());
        }else {
            update = lambdaUpdate().eq(User::getId, userInfoDTO.getId()).set(User::getStatus, 1).update();
        }
        return update;
    }

    /**
     * 获取除管理员以外的用户信息
     * @return 除管理员以外的用户信息
     */
    @Override
    public List<UserVo> getUser() {
        List<User> userList = lambdaQuery().eq(User::getIsAdmin, 1).list();
        return BeanUtil.copyToList(userList, UserVo.class);
    }

    /**
     * 发放token
     * @param users 用户信息
     * @return 带token的用户信息
     */
    private UserLoginVo getUserLoginAndToken(User users) {
        //发放token
        Map<String, Object> claim = new HashMap<>();
        claim.put(JwtConst.USER_ID, users.getId());
        String token = JwtUtil.getToken(claim);
        //将token存入redis中,有效期为20分钟
        stringRedisTemplate.opsForValue().set(JwtConst.USER_TOKEN_CATCH+users.getId().toString(),JwtConst.PREFIX+token, Duration.ofMinutes(20));
        return UserLoginVo.builder()
                .id(users.getId())
                .username(users.getUsername())
                .isAdmin(users.getIsAdmin())
                .token(token)
                .build();
    }
}
